package com.ruoyi.datastatistics.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basedata.domain.MeterSheets;
import com.ruoyi.basedata.domain.TerminalsXb;
import com.ruoyi.basedata.domain.UsersXb;
import com.ruoyi.basedata.service.IMeterSheetsService;
import com.ruoyi.basedata.service.ITerminalsXbService;
import com.ruoyi.basedata.service.IUsersXbService;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PageUtil;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.datastatistics.domain.TerminalsFreezeXb;
import com.ruoyi.datastatistics.mapper.TerminalsFreezeXbMapper;
import com.ruoyi.datastatistics.model.MeterConsumptionQueryModel;
import com.ruoyi.datastatistics.model.MeterConsumptionResultModel;
import com.ruoyi.datastatistics.service.ITerminalsFreezeXbService;
import com.ruoyi.datastatistics.vo.TerminalUsageVO;
import com.ruoyi.datastatistics.wxentity.WXTerminalsFreezeXb;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.Month;
import java.time.YearMonth;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 小冻结数据（设备维度）Service业务层处理
 *
 * @author ruoyi
 * @date 2023-09-14
 */
@Service
@Slf4j
public class TerminalsFreezeXbServiceImpl extends ServiceImpl<TerminalsFreezeXbMapper,TerminalsFreezeXb> implements ITerminalsFreezeXbService {
    @Autowired
    private TerminalsFreezeXbMapper terminalsFreezeXbMapper;

    @Autowired
    private IMeterSheetsService meterSheetsService;

    @Autowired
    private ITerminalsXbService terminalsXbService;

    @Autowired
    private IUsersXbService usersXbService;
    /**
     *日冻结类型
     */
    private static final String FROZEN_TYPE_DAY = "day";
    /**
     *月冻结类型
     */
    private static final String FROZEN_TYPE_MONTH = "month";

    /**
     * 查询小冻结数据（设备维度）
     *
     * @param ID 小冻结数据（设备维度）主键
     * @return 小冻结数据（设备维度）
     */
    @Override
    public TerminalsFreezeXb selectTerminalsFreezeXbByID(Long ID) {
        return terminalsFreezeXbMapper.selectTerminalsFreezeXbByID(ID);
    }

    /**
     * 查询小冻结数据（设备维度）列表
     *
     * @param terminalsFreezeXb 小冻结数据（设备维度）
     * @return 小冻结数据（设备维度）
     */
    @Override
    public List<TerminalsFreezeXb> selectTerminalsFreezeXbList(TerminalsFreezeXb terminalsFreezeXb) {
        return terminalsFreezeXbMapper.selectTerminalsFreezeXbList(terminalsFreezeXb);
    }

    /**
     * 新增小冻结数据（设备维度）
     *
     * @param terminalsFreezeXb 小冻结数据（设备维度）
     * @return 结果
     */
    @Override
    public int insertTerminalsFreezeXb(TerminalsFreezeXb terminalsFreezeXb) {
        return terminalsFreezeXbMapper.insertTerminalsFreezeXb(terminalsFreezeXb);
    }

    /**
     * 修改小冻结数据（设备维度）
     *
     * @param terminalsFreezeXb 小冻结数据（设备维度）
     * @return 结果
     */
    @Override
    public int updateTerminalsFreezeXb(TerminalsFreezeXb terminalsFreezeXb) {
        return terminalsFreezeXbMapper.updateTerminalsFreezeXb(terminalsFreezeXb);
    }

    /**
     * 批量删除小冻结数据（设备维度）
     *
     * @param IDs 需要删除的小冻结数据（设备维度）主键
     * @return 结果
     */
    @Override
    public int deleteTerminalsFreezeXbByIDs(Long[] IDs) {
        return terminalsFreezeXbMapper.deleteTerminalsFreezeXbByIDs(IDs);
    }

    /**
     * 删除小冻结数据（设备维度）信息
     *
     * @param ID 小冻结数据（设备维度）主键
     * @return 结果
     */
    @Override
    public int deleteTerminalsFreezeXbByID(Long ID) {
        return terminalsFreezeXbMapper.deleteTerminalsFreezeXbByID(ID);
    }

    @Override
    public List<WXTerminalsFreezeXb> WxList(String startTime, String endTime, String reportType, String terminalId) {
        return terminalsFreezeXbMapper.WxList(startTime, endTime, reportType, terminalId);
    }

    @Override
    public List<WXTerminalsFreezeXb> WxListOrderByFlow(String startTime, String endTime, String reportType, String terminalId) {
        return terminalsFreezeXbMapper.WxListOrderByFlow(startTime, endTime, reportType, terminalId);
    }

    @Override
    public Map<String, Object> getWaterConsumptionList(MeterConsumptionQueryModel model) {
        Map<String, Object> resultMap = new HashMap<>();
        List<MeterConsumptionResultModel> dataList = new ArrayList<>();

        //得到所有小表区域
        List<MeterSheets> meterSheetsList = meterSheetsService.selectMeterSheetsBySheetsCode(model.getSheetsCode());
        Map<String, String> meterSheetCodeNameMap = meterSheetsList.stream().collect(Collectors.toMap(MeterSheets::getSheetsCode, MeterSheets::getSheetsName));

        //得到所有表
        List<TerminalsXb> terminalsXbList = terminalsXbService.selectTerminalsXbBySheetsCode(model.getSheetsCode());
        Map<String, TerminalsXb> terminalsXbTerminalIdSheetsCodeMap = terminalsXbList.stream().collect(Collectors.toMap(TerminalsXb::getLocationNb, item -> item));

        List<UsersXb> usersXbList = usersXbService.selectUsersXbList(new UsersXb());
        Map<String, String> userXbUserIdNameMap = usersXbList.stream().collect(Collectors.toMap(UsersXb::getUserId, UsersXb::getUserName));
        Map<String, String> userXbUserIdAddressMap = usersXbList.stream().collect(HashMap::new, (map, item) -> map.put(item.getUserId(), item.getUserAddress()), HashMap::putAll);

        LambdaQueryWrapper<TerminalsFreezeXb> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TerminalsFreezeXb::getFrozenType,FROZEN_TYPE_DAY);
        if (model.getConsumptionFrom() != null) {
            queryWrapper.ge(TerminalsFreezeXb::getCumulativeFlow, model.getConsumptionFrom());
        }
        if (model.getConsumptionTo() != null) {
            queryWrapper.le(TerminalsFreezeXb::getCumulativeFlow, model.getConsumptionTo());
        }
        if (model.getFreezeTimeFrom() != null) {
            queryWrapper.ge(TerminalsFreezeXb::getFreezeTime, model.getFreezeTimeFrom());
        }
        if (model.getFreezeTimeTo() != null) {
            queryWrapper.le(TerminalsFreezeXb::getFreezeTime, model.getFreezeTimeTo());
        }

//        List<String> terminalIdList = terminalsXbList.stream().map(TerminalsXb::getTerminalId).collect(Collectors.toList());
//        queryWrapper.in(TerminalsFreezeXb::getTerminalId, terminalIdList);
//        queryWrapper.exists(model.getSheetsCode() == null ? "select terminal_id from t_terminals_xb where sheets_code is not null and del_flag=0"
//                : "select terminal_id from t_terminals_xb where sheets_code is not null and del_flag=0 and sheets_code={0}", model.getSheetsCode());

        List<TerminalsFreezeXb> terminalsFreezeXbList = terminalsFreezeXbMapper.selectList(queryWrapper);
        if (CollUtil.isNotEmpty(terminalsFreezeXbList)) {
            Map<String, List<TerminalsFreezeXb>> terminalIdFreezeDataMap = terminalsFreezeXbList.stream().filter(t -> t.getLocationNb() != null).collect(Collectors.groupingBy(TerminalsFreezeXb::getLocationNb));
            terminalIdFreezeDataMap.forEach((locationNb, terminalIdFreezeDataList) -> {
                TerminalsXb terminalsXb = terminalsXbTerminalIdSheetsCodeMap.get(locationNb);
                if (terminalsXb != null) {
                    MeterConsumptionResultModel resultModel = new MeterConsumptionResultModel();
                    resultModel.setLocationNb(locationNb);
                    resultModel.setTerminalId(terminalsXb.getTerminalId());
                    resultModel.setUserId(terminalsXb.getUserId());
                    resultModel.setUserName(userXbUserIdNameMap.get(terminalsXb.getUserId()));
                    resultModel.setInstallAddress(terminalsXb.getInstallAddress() != null ? terminalsXb.getInstallAddress() : userXbUserIdAddressMap.get(terminalsXb.getUserId()));
                    resultModel.setSheetsCode(terminalsXb.getSheetsCode());
                    resultModel.setSheetId(terminalsXb.getSheetsId());
                    resultModel.setSheetsName(meterSheetCodeNameMap.get(resultModel.getSheetsCode()));
                    double consumption = CollUtil.isEmpty(terminalIdFreezeDataList) ? 0D : terminalIdFreezeDataList.stream().mapToDouble(item -> item.getCumulativeFlow() == null ? 0D : item.getCumulativeFlow().doubleValue()).sum();
                    resultModel.setConsumption(NumberUtil.round(consumption, 2).doubleValue());
                    dataList.add(resultModel);
                }
            });

            Comparator<MeterConsumptionResultModel> comparator = Comparator.comparing(MeterConsumptionResultModel::getSheetId);
            if ("sheetsName".equals(model.getOrderByColumn())) {
                comparator = Comparator.comparing(MeterConsumptionResultModel::getSheetsName);
            }
            if ("userId".equals(model.getOrderByColumn())) {
                comparator = Comparator.comparing(MeterConsumptionResultModel::getUserId);
            }
            if ("userName".equals(model.getOrderByColumn())) {
                comparator = Comparator.comparing(MeterConsumptionResultModel::getUserName);
            }
            if ("terminalId".equals(model.getOrderByColumn())) {
                comparator = Comparator.comparing(MeterConsumptionResultModel::getTerminalId);
            }
            if ("installAddress".equals(model.getOrderByColumn())) {
                comparator = Comparator.comparing(MeterConsumptionResultModel::getInstallAddress);
            }
            if ("consumption".equals(model.getOrderByColumn())) {
                comparator = Comparator.comparing(MeterConsumptionResultModel::getConsumption);
            }
            if ("descending".equals(model.getIsAsc())) {
                comparator = comparator.reversed();
            }

            dataList.sort(comparator);
        }

        double dataConsumption = CollUtil.isEmpty(dataList) ? 0D : dataList.stream().mapToDouble(item -> item.getConsumption() == null ? 0D : item.getConsumption()).sum();
        resultMap.put("dataTotal", dataList.size());
        resultMap.put("dataRows", PageUtil.startPage(dataList, model.getPageNum(), model.getPageSize()));
        resultMap.put("dataConsumption", NumberUtil.round(dataConsumption, 2).doubleValue());

        return resultMap;
    }

    @Override
    public List<TerminalsFreezeXb> listThirtyDaysFlow(TerminalsFreezeXb terminalsFreezeXb) {
        return terminalsFreezeXbMapper.selectList(new QueryWrapper<TerminalsFreezeXb>().eq("location_nb", terminalsFreezeXb.getLocationNb())
                .eq("frozen_type", FROZEN_TYPE_DAY).ge("freeze_time", terminalsFreezeXb.getStartTime()).le("freeze_time", terminalsFreezeXb.getEndTime())
                .orderByDesc("freeze_time").last(" limit 30"));
    }

    @Override
    public List<TerminalUsageVO> getUsageBySheetsIdAndDate(TerminalUsageVO usage) {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        if (Constants.SYS_FLAG_ADMIN != user.getSystemFlag()) {
            usage.setSysUserId(user.getUserId());
        }
        if (Constants.SYS_FLAG_NORMAL_USER == user.getSystemFlag()) {
            usage.setTenantId(user.getTenantId());
        }
        List<TerminalUsageVO> list = terminalsFreezeXbMapper.selectTerminalUsageList(usage);
        if (CollectionUtils.isNotEmpty(list)) {
            Set<Long> sheetsIds = list.stream().map(TerminalUsageVO::getSheetsId).collect(Collectors.toSet());

            List<TerminalUsageVO> usageGroupSheetsId = terminalsFreezeXbMapper.selectTerminalUsageGroupBySheetsIdAndDate(TerminalUsageVO.builder().beginDate(usage.getBeginDate()).endDate(usage.getEndDate()).sheetsIds(new ArrayList<>(sheetsIds)).build());
            for (TerminalUsageVO all : list) {
                for (TerminalUsageVO total : usageGroupSheetsId) {
                    if (Objects.equals(all.getSheetsId(), total.getSheetsId()) && Objects.equals(all.getDate(), total.getDate())) {
                        all.setTotalUsage(total.getUsage());
                        break;
                    }
                }
            }
        }
        return list;
    }

    @Override
    public List<TerminalUsageVO> getUsageBySheetsIdAndDateWithoutTerminal(TerminalUsageVO usage) {
        if (StringUtils.isNoneBlank(usage.getBeginDate(), usage.getEndDate())) {
            LocalDate begin = LocalDate.parse(usage.getBeginDate());
            LocalDate end = LocalDate.parse(usage.getEndDate());
            if (begin.isAfter(end)) {
                throw new ServiceException("开始日期不能晚于结束日期");
            } else if (end.compareTo(begin) > Month.JANUARY.maxLength()) {
                throw new ServiceException(String.format("时间间隔最长不能超过%d天", Month.JANUARY.maxLength()));
            }
        }
        List<TerminalUsageVO> list = terminalsFreezeXbMapper.selectTerminalUsageGroupBySheetsIdAndDate(usage);
        // 不根据日期分组
        List<TerminalUsageVO> listNotGroupByDate = this.getService().getUsageBySheetsIdWithoutTerminal(usage);
        for (TerminalUsageVO u : list) {
            for (TerminalUsageVO notGroupByDate : listNotGroupByDate) {
                if (Objects.equals(u.getSheetsId(), notGroupByDate.getSheetsId())) {
                    u.setTotalUsage(notGroupByDate.getUsage());
                    break;
                }
            }
        }
        return list;
    }

    @Override
    public List<TerminalUsageVO> getUsageBySheetsIdWithoutTerminal(TerminalUsageVO usage) {
        // 不根据日期分组
        usage.setGroupByDate(false);
        return terminalsFreezeXbMapper.selectTerminalUsageGroupBySheetsIdAndDate(usage);
    }

    /**
     * {@inheritDoc}
     *
     * @param dateStr {@inheritDoc}
     */
    @Override
    public void generateFreezeDataXb(String dateStr) {
        List<String> dates = new ArrayList<>(31);
        String frozenType = getDateListAndFrozenType(dateStr, dates);
        List<CompletableFuture<Boolean>> futures = new ArrayList<>(dates.size());
        if (FROZEN_TYPE_DAY.equals(frozenType)) {
            for (String date : dates) {
                CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                    List<TerminalsFreezeXb> freezeXbList = terminalsFreezeXbMapper.selectDailyFrozenDataXbByDate(date);
                    freezeXbList = freezeXbList.stream().filter(e -> StringUtils.isNotBlank(e.getLocationNb())).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(freezeXbList)) {
                        terminalsFreezeXbMapper.insertBatch(freezeXbList);
                    }
                    return true;
                }).exceptionally(ex -> {
                    log.error("生成{}日冻结数据出错", date, ex);
                    return false;
                });
                futures.add(future);
            }
        } else {
            for (String date : dates) {
                CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                    List<TerminalsFreezeXb> freezeXbList = terminalsFreezeXbMapper.selectMonthlyFrozenDataXbByYearMonth(date);
                    freezeXbList = freezeXbList.stream().filter(e -> StringUtils.isNotBlank(e.getLocationNb())).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(freezeXbList)) {
                        terminalsFreezeXbMapper.insertBatch(freezeXbList);
                    }
                    return true;
                }).exceptionally(ex -> {
                    log.error("生成{}月冻结数据出错", date, ex);
                    return false;
                });
                futures.add(future);
            }
        }
        CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        CompletableFuture<List<Boolean>> allResults = allOf.thenApply(v ->
                futures.stream()
                        .map(CompletableFuture::join)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList())
        );
        // 获取并打印所有结果
        try {
            List<Boolean> results = allResults.get();
            List<Boolean> falseList = results.stream().filter(e -> !e).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(falseList)) {
                log.error("有未成功生成的冻结数据");
            }
        } catch (Exception ignored) {
        }
        // int pageSize = 5000;
        // long total = terminalsFreezeXbMapper.countNoFrozenXb(frozenType, dates);
        // int pages = (int) Math.ceil((double) total / pageSize);
        // for (int i = 0; i < pages; i++) {
        //     // 所有缺冻结数据的表的locationNb和日期
        //     PageUtils.startPage(1, pageSize, false);
        //     List<TerminalsFreezeXb> list = terminalsFreezeXbMapper.selectNoFrozenXb(frozenType, dates);
        //     int batchSize = 50;
        //     if (CollectionUtils.isNotEmpty(list)) {
        //         List<List<TerminalsFreezeXb>> lists = CollectionUtil.split(list, batchSize);
        //         List<CompletableFuture<Boolean>> futures = new ArrayList<>(lists.size());
        //         for (List<TerminalsFreezeXb> e : lists) {
        //             CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        //                 List<TerminalsFreezeXb> freezeXbList = terminalsFreezeXbMapper.selectDailyFrozenDataXb(e);
        //                 terminalsFreezeXbMapper.insertBatch(freezeXbList);
        //                 return true;
        //             }).exceptionally(ex -> {
        //                 log.error("生成日冻结数据出错", ex);
        //                 return false;
        //             });
        //             futures.add(future);
        //         }
        //         CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        //         CompletableFuture<List<Boolean>> allResults = allOf.thenApply(v ->
        //                 futures.stream()
        //                         .map(CompletableFuture::join)
        //                         .filter(Objects::nonNull)
        //                         .collect(Collectors.toList())
        //         );
        //
        //         // 获取并打印所有结果
        //         try {
        //             List<Boolean> results = allResults.get();
        //             List<Boolean> falseList = results.stream().filter(e -> !e).collect(Collectors.toList());
        //             if (CollectionUtils.isNotEmpty(falseList)) {
        //                 log.error("有未成功生成的冻结数据");
        //             }
        //         } catch (Exception ignored) {
        //         }
        //     }
        // }
    }

    /**
     * 根据给定的日期或年月字符串获取冻结类型并返回日期或年月序列
     *
     * @param dateStr 给定的日期或年月字符串
     * @param dates   返回日期或年月序列
     * @return 冻结类型
     */
    private String getDateListAndFrozenType(String dateStr, List<String> dates) {
        String begin, end, frozenType;
        if (StringUtils.isNotBlank(dateStr)) {
            if (dateStr.contains(Constants.DATE_RANGE_CONNECTOR)) {
                String s1 = dateStr.split(Constants.DATE_RANGE_CONNECTOR)[0].trim();
                String s2 = dateStr.split(Constants.DATE_RANGE_CONNECTOR)[1].trim();
                try {
                    LocalDate d1 = LocalDate.parse(s1);
                    LocalDate d2 = LocalDate.parse(s2);
                    // 范围是闭区间，所以+1
                    if (Math.abs(d1.compareTo(d2)) + 1 > Month.JANUARY.maxLength()) {
                        throw new ServiceException("最多生成31天数据！！");
                    }
                    begin = d1.toString();
                    end = d2.toString();
                    frozenType = FROZEN_TYPE_DAY;
                    dates.addAll(DateUtils.generateDateStringListBetweenTwoDate(begin, end));
                } catch (DateTimeParseException ignored) {
                    try {
                        YearMonth y1 = YearMonth.parse(s1);
                        YearMonth y2 = YearMonth.parse(s2);
                        // 范围是闭区间，所以+1
                        if (Math.abs(y1.compareTo(y2)) + 1 > Month.values().length) {
                            throw new ServiceException("最多生成12个月数据！！");
                        }
                        begin = y1.toString();
                        end = y2.toString();
                        frozenType = FROZEN_TYPE_MONTH;
                        dates.addAll(DateUtils.generateYearMonthStringListBetweenTwoYearMonth(begin, end));
                    } catch (DateTimeParseException ignored1) {
                        throw new ServiceException("指定日期或年月格式不正确！！");
                    }
                }
            } else {
                try {
                    LocalDate localDate = LocalDate.parse(dateStr);
                    begin = localDate.toString();
                    frozenType = FROZEN_TYPE_DAY;
                } catch (DateTimeParseException ignored) {
                    try {
                        YearMonth yearMonth = YearMonth.parse(dateStr);
                        begin = yearMonth.toString();
                        frozenType = FROZEN_TYPE_MONTH;
                    } catch (DateTimeParseException ignored1) {
                        throw new ServiceException("指定日期或年月格式不正确！！");
                    }
                }
                dates.add(begin);
            }
        } else {
            begin = LocalDate.now().minusDays(1).toString();
            frozenType = FROZEN_TYPE_DAY;
            dates.add(begin);
        }
        return frozenType;
    }

    private TerminalsFreezeXbServiceImpl getService(){
        return SpringUtils.getBean(this.getClass());
    }
}
